#include "video_receiver.h"
#include <mutex>
#include <QtConcurrent>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}

#include <QDebug>

VideoReceiver::VideoReceiver(QObject *parent)
    : QObject(parent)
{
    static std::once_flag flag;
    std::call_once(flag, []{ avformat_network_init(); });
}


void VideoReceiver::startReceive(const QString &url)
{
    m_stop = false;
    QtConcurrent::run([&, url]{ run(url); }); // 丢到线程池
}

void VideoReceiver::stop()
{
    m_stop = true;
}

/* 真正的解码循环 */
void VideoReceiver::run(const QString &url)
{
    AVFormatContext *fmtCtx = nullptr;
    if (avformat_open_input(&fmtCtx, url.toUtf8().constData(), nullptr, nullptr) < 0) {
        qWarning() << "Can not open input";
        return;
    }
    if (avformat_find_stream_info(fmtCtx, nullptr) < 0) {
        qWarning() << "Can not find stream info";
        avformat_close_input(&fmtCtx);
        return;
    }

    int vIdx = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (vIdx < 0) { qWarning() << "No video stream"; avformat_close_input(&fmtCtx); return; }

    AVCodecParameters *codecPar = fmtCtx->streams[vIdx]->codecpar;
    const AVCodec *codec = avcodec_find_decoder(codecPar->codec_id);
    AVCodecContext *codecCtx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codecCtx, codecPar);
    avcodec_open2(codecCtx, codec, nullptr);

    SwsContext *swsCtx = sws_getContext(
        codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
        codecCtx->width, codecCtx->height, AV_PIX_FMT_RGB24,
        SWS_BILINEAR, nullptr, nullptr, nullptr);

    AVPacket *pkt   = av_packet_alloc();
    AVFrame *frame  = av_frame_alloc();
    AVFrame *rgb    = av_frame_alloc();
    int bufSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24,
                                           codecCtx->width, codecCtx->height, 1);
    uint8_t *buf = (uint8_t*)av_malloc(bufSize);
    av_image_fill_arrays(rgb->data, rgb->linesize, buf,
                         AV_PIX_FMT_RGB24, codecCtx->width, codecCtx->height, 1);

    while (!m_stop) {
        if (av_read_frame(fmtCtx, pkt) < 0) break;   // 读到 EOF 或错误
        if (pkt->stream_index == vIdx) {
            avcodec_send_packet(codecCtx, pkt);
            if (avcodec_receive_frame(codecCtx, frame) == 0) {
                sws_scale(swsCtx, frame->data, frame->linesize, 0,
                          codecCtx->height, rgb->data, rgb->linesize);
                QImage img(buf, codecCtx->width, codecCtx->height,
                           QImage::Format_RGB888);
                emit newFrame(img.copy());
            }
        }
        av_packet_unref(pkt);
    }

    av_free(buf);
    av_frame_free(&frame);
    av_frame_free(&rgb);
    av_packet_free(&pkt);
    sws_freeContext(swsCtx);
    avcodec_free_context(&codecCtx);
    avformat_close_input(&fmtCtx);
}
